package com.motu.vertx.module.utility.main;

import com.motu.data.MotuAdManager;
import com.motu.data.MotuBiBaseManager;
import com.motu.vertx.module.utility.ClassReload.Scanner;
import com.motu.vertx.module.utility.base.BaseServerParam;
import com.motu.vertx.module.utility.base.ServerConfig;
import com.motu.vertx.module.utility.commondata.CommonDataManager;
import com.motu.vertx.module.utility.exception.LockException;
import com.motu.vertx.module.utility.kafka.KafkaLogManager;
import com.motu.vertx.module.utility.logsdk.KafkaBiManager;
import com.motu.vertx.module.utility.logsdk.LogSDKManager;
import com.motu.vertx.module.utility.logsdk.ThinkingDataBaseManager;
import com.motu.vertx.module.utility.model.DbManager;
import com.motu.vertx.module.utility.monitor.MonitorManager;
import com.motu.vertx.module.utility.mysql.MysqlClient;
import com.motu.vertx.module.utility.mysql.MysqlTool;
import com.motu.vertx.module.utility.redis.RedisTools;
import com.motu.vertx.module.utility.toolset.ErrorWordManager;
import com.motu.vertx.module.utility.toolset.SensitivewordEngine;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.shareddata.Lock;
import io.vertx.core.shareddata.SharedData;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * 游戏启动初始化公共配置信息
 */
public class MainService {

    public final static Logger logger = LoggerFactory.getLogger("MainService");

    private static Vertx vertx;// vertx对象

	private static WebClient webClient;

    public static Vertx getVertx() {
        return vertx;
    }

	public static WebClient getWebClient() {
		return webClient;
	}

    /**
     * 初始化公共服务
     */
    public static void init(Vertx vertx, JsonObject config) throws Throwable {

        MainService.vertx = vertx;

		webClient = WebClient.create(vertx, new WebClientOptions().setKeepAlive(true));

        // 初始化服务器配置
        ServerConfig.init(config);

        // 初始化数据库连接
        MysqlClient.init(vertx);

        // 初始化redis连接
        Future.await(RedisTools.init(vertx));

        // 初始化服务端系统参数（要等服务端系统参数初始化好之后才能初始化其他数据）
        Future.await(initBaseServerParams());

		// 判断必须配置的服务端参数
		if (!BaseServerParam.check()) {
			throw new Exception("ServerParam check error!!!");
		}

		List<Future> futures = new ArrayList<>();

        // 初始化公共数据
        CommonDataManager.init(futures);

        // 监控配置初始化
        MonitorManager.init(futures);

		Future.await(CompositeFuture.all(futures));

        // 初始化错误语提示
        ErrorWordManager.init();

        // 初始化敏感字库
        SensitivewordEngine.init();

        /******************** 初始化kafka配置 ********************/

        JsonObject appConfig = ServerConfig.getAppConfig();

        // 初始化游戏数据kafka配置
        DbManager.init(vertx, appConfig);

		// 初始化游戏日志kafka配置（消耗获得日志/错误码日志等）
		KafkaLogManager.init(vertx, appConfig);

        // 初始化第三方sdk数据kafka配置（数数/热云等）
        LogSDKManager.init(vertx, appConfig);

		// 初始化魔兔BIkafka配置
		MotuBiBaseManager.init(vertx, appConfig);

		// 初始化魔兔广告kafka配置
		MotuAdManager.init(vertx, appConfig);

        // 初始化bi-kafka配置
        KafkaBiManager.init(vertx, appConfig);

        // 初始化数数sdk
		ThinkingDataBaseManager.init(appConfig);

        //初始化热更代码
        if (appConfig.containsKey("jarName")){
            Scanner.getInstance().init(appConfig.getString("jarName"));
        }

        logger.info("MainService init success!!!");

    }

    /**
     * 初始化服务端系统参数
     */
    public static Future<Void> initBaseServerParams() {
        Promise<Void> promise = Promise.promise();
        String sql = "SELECT name,value FROM server_params;";
        MysqlTool.queryJsonArrayFromDb(MysqlClient.getServerPool(), sql, new JsonArray(), res -> {
            if (res.succeeded()) {
                JsonArray list = res.result();
                logger.info("initBaseServerParams size:" + list.size());
                Class<BaseServerParam> clazz = BaseServerParam.class;// 通过反射修改Function的变量
                reflectParam(clazz, list);
                promise.complete();
            } else {
                logger.error("initBaseServerParams error cause:" + res.cause().getMessage());
                promise.fail(res.cause());
            }
        });
        return promise.future();
    }

    private static void reflectParam(Class<BaseServerParam> clazz, JsonArray list) {
        for (int i = 0, size = list.size(); i < size; i++) {
            JsonObject obj = list.getJsonObject(i);
            String name = obj.getString("name");
            String value = obj.getString("value");
            try {
                Field field = clazz.getDeclaredField(name);
                setServerParamFieldValue(clazz, field, value);
				logger.info("BaseServerParam name:" + name + " value:" + value);
            }catch (NoSuchFieldException ne) {
				// 这里会获取到ServerParam类中的字段，不用处理
			} catch (Exception e) {
                logger.error("initBaseServerParams error i = " + i + " name = " + name + " value = " + value);
                if (name.indexOf(' ') != -1) {
                    logger.error(name + "参数名中有空格!!!!");
                }
                if (value.indexOf(' ') != -1) {
                    logger.error(value + "参数数值中有空格!!!!");
                }
            }
        }
    }

	/**
	 * 设置服务端系统参数字段值
	 * @param clazz
	 * @param field
	 * @param value
	 * @throws Exception
	 */
	public static void setServerParamFieldValue(Class clazz, Field field, String value) throws Exception{
		if (field.getType() == int.class) {
			field.set(clazz, Tool.stringToInt(value));
		} else if (field.getType() == int[].class) {
			field.set(clazz, Tool.strToIntArray(value, ";"));
		} else if (field.getType() == String[].class) {
			field.set(clazz, value.replace(" ", "").split(";"));
		} else if (field.getType() == double[].class) {
			field.set(clazz, Tool.strToDoubleArray(value, ";"));
		} else if (field.getType() == String.class) {
			field.set(clazz, value);
		} else if (field.getType() == double.class) {
			field.set(clazz, Double.parseDouble(value));
		} else if (field.getType() == long.class) {
			field.set(clazz, Long.parseLong(value));
		}
	}

	/**
	 * 获取vertx锁
	 * 注意：获取锁的操作需要发生在数据更新之前，防止获取锁超时抛出异常导致数据更新异常
	 * @param key 锁关键字
	 */
	public static Lock getVertLock(String key) throws Exception {
		return getVertLock(key, 3000);// 默认三秒钟
	}

	/**
	 * 获取vertx锁
	 *
	 * @param key     锁关键字
	 * @param timeout 获取锁等待超时时间（相当于加锁逻辑执行的时间上限）（毫秒）
	 */
	public static Lock getVertLock(String key, long timeout) throws Exception {
		SharedData sd = vertx.sharedData();
		Lock lock;
		try {
			lock = Future.await(sd.getLockWithTimeout(key, timeout));
		} catch (Exception e) {
			throw new LockException(e);
		}
		return lock;
	}

	/**
	 * 释放vertx锁
	 *
	 * @param lock
	 */
	public static void releaseVertLock(Lock lock) {
		if (lock != null) {
			lock.release();
		}
	}


}
